/**
 *  \file kwptp.c
 *
 *  \brief
 *
 *  \author   <@linde-mh.de>
 *
 *
 */

/*===============================================[ private includes  ]================================================*/
#define KWPTP_LINKAGE
#include "kwptp.h"
#include <stddef.h>
#include <string.h>
#include <stdio.h>

/*===============================================[ private defines   ]================================================*/

/*===============================================[ private datatypes ]================================================*/

/*===============================================[ private variables ]================================================*/

/*===============================================[ public variables  ]================================================*/

/*===============================================[ private functions ]================================================*/
static RC kwptp_state_idle(struct kwptp *me, struct kwptp_can_msg *msg);
static RC kwptp_state_expect_response(struct kwptp *me, struct kwptp_can_msg *msg);
static RC kwptp_state_flow_control(struct kwptp *me, struct kwptp_can_msg *msg);

static DWORD WINAPI kwptp_timer_thread(LPVOID par);
static RC kwptp_create_timer_thread(struct kwptp *me);

/*====================================================================================================================*/

/**
 *  Initialization.
 *
 *  Initialize the kwptp module.
 *
 *  \return RC_SUCCESS if successful.
 */
RC kwptp_initialize(struct kwptp *me, kwptp_transmit_function_t transmit, void *transmit_obj) {
   me->state = kwptp_state_idle;
   me->transmit = transmit;
   me->transmit_obj = transmit_obj;
   me->use_manual_st_min = FALSE;
   me->manual_st_min = 0;
   kwptp_create_timer_thread(me);
   return RC_SUCCESS;
}

RC kwptp_transmit(struct kwptp *me, struct kwptp_msg *msg) {
   me->current_msg = msg;
   me->current_msg->state = kwptp_msg_state_sending;

   struct kwptp_can_msg can_msg;
   can_msg.dlc = 8;

   if (msg->len > 7) {                                /* segmented transfer, first frame */
      can_msg.data[0] = 0x10 | (msg->len >> 8);
      can_msg.data[1] = msg->len & 0xff;
      memcpy(&can_msg.data[2], &msg->data[0], 6);

      me->state = kwptp_state_flow_control;
      me->nextIndex = 6;
   } else {                                           /* single frame message */
      can_msg.data[0] = msg->len;
      memcpy(&can_msg.data[1], &msg->data[0], msg->len);

      me->state = kwptp_state_expect_response;
   }

   me->transmit(me->transmit_obj, &can_msg);
   me->timer = msg->timeout;

   return RC_SUCCESS;
}

RC kwptp_process(struct kwptp *me, struct kwptp_can_msg *msg) {
   me->state(me, msg);
   return RC_SUCCESS;
}

static RC kwptp_state_idle(struct kwptp *me, struct kwptp_can_msg *msg) {
   return RC_ERROR;
}

static RC kwptp_state_flow_control(struct kwptp *me, struct kwptp_can_msg *msg) {
   struct kwptp_can_msg can_msg;
   can_msg.dlc = 8;

   BO framesLimited = TRUE;
   U8 framesRemaining = msg->data[1];
   if (framesRemaining == 0) {
      framesLimited = FALSE;
   }

   U32 st_min;
   if (me->use_manual_st_min == TRUE) {
      /* separation time manual override */
      st_min = me->manual_st_min;
   } else {
      /* requested minimum separation time between consecutive frames */
      st_min = msg->data[2];
   }

   U8 current_bytes;
   while (me->nextIndex < me->current_msg->len && (framesRemaining > 0 || framesLimited == FALSE)) {
      Sleep(st_min);

      /* calculate how many bytes will be sent in this message */
      if (me->current_msg->len - me->nextIndex >= 7) {
         current_bytes = 7;
      } else {
         current_bytes = me->current_msg->len - me->nextIndex;
      }

      /* The part behind the OR (|) calculates the running number of the consecutive frame based on the 'nextIndex'
       * variable. Note that 'nextIndex' will take values of 6 + x * 7 where x is a positive integer:
       * nextIndex = 6        --> ((  6 + 1) / 7) % 16 = 0x01
       * nextIndex = 13       --> (( 13 + 1) / 7) % 16 = 0x02
       * nextIndex = 20       --> (( 21 + 1) / 7) % 16 = 0x03
       *                     .....
       * nextIndex = 104      --> ((104 + 1) / 7) % 16 = 0x0F
       * nextIndex = 111      --> ((111 + 1) / 7) % 16 = 0x00
       * nextIndex = 118      --> ((111 + 1) / 7) % 16 = 0x01
       *                     .....
       */
      can_msg.data[0] = 0x20 | (((me->nextIndex + 1) / 7) % 16);
      memcpy(&can_msg.data[1], &me->current_msg->data[me->nextIndex], current_bytes);

      framesRemaining--;
      me->nextIndex += current_bytes;

      me->transmit(me->transmit_obj, &can_msg);
   }
   me->timer = me->current_msg->timeout;

   if (me->nextIndex == me->current_msg->len) {
      me->state = kwptp_state_expect_response;        /* whole message sent -> expect pos./neg. response */
   } else {
      me->state = kwptp_state_flow_control;           /* message only partially sent -> expect flow control */
   }

   return RC_SUCCESS;
}

static RC kwptp_state_expect_response(struct kwptp *me, struct kwptp_can_msg *can_msg) {

   me->timer = 0; /* disable timeout */

   /* Wait for arbitrarily pending messages and return first non pending message */
   if ((can_msg->data[0] == 0x03) && (can_msg->data[1] == 0x7f) && (can_msg->data[3] == 0x78)) {
      /*reload timer */
      me->current_msg->state = kwptp_msg_state_pending;
      me->timer = me->current_msg->timeout;
      return RC_SUCCESS;
   }

   switch (can_msg->data[0] >> 4) {
      case 0: {         /* single frame */
         me->current_msg->len = can_msg->data[0] & 0x0F;
         memcpy(&me->current_msg->data[0], &can_msg->data[1], me->current_msg->len);

         if ((can_msg->data[0] == 0x03) && (can_msg->data[1] == 0x7f)) {
            me->current_msg->state = kwptp_msg_state_negative_response;
         } else {
            me->current_msg->state = kwptp_msg_state_positive_response;
         }

         me->state = kwptp_state_idle;
         if (NULL != me->current_msg->callback) {
            me->current_msg->callback(me->current_msg->callback_data, me->current_msg);
         }
         break;
      }
      case 1: {         /* first frame */
         me->current_msg->len = ((can_msg->data[0] & 0x0F) << 8) + can_msg->data[1];
         memcpy(&me->current_msg->data[0], &can_msg->data[2], 6); /* first frame contains 6 data bytes */
         me->nextIndex = 6;

         /* First frames are always positive responses because negative responses are always single frames. */
         me->current_msg->state = kwptp_msg_state_positive_response;

         /* send flow control frame */
         struct kwptp_can_msg tempMsg;
         tempMsg.dlc = 8;
         tempMsg.data[0] = 0x30;
         tempMsg.data[1] = 0x00;         /* no maximum frame count */
         tempMsg.data[2] = 0x00;         /* st_min = 0 */
         me->state = kwptp_state_expect_response;
         me->transmit(me->transmit_obj, &tempMsg);
         me->timer = me->current_msg->timeout;

         break;
      }
      case 2: {         /* consecutive frame */
         U8 currentBytes;

         /* calculate how many bytes will be received in this message */
         if (me->current_msg->len - me->nextIndex >= 7) {
            currentBytes = 7;
         } else {
            currentBytes = me->current_msg->len - me->nextIndex;
         }

         /* copy data from incoming message to receive buffer */
         memcpy(&me->current_msg->data[me->nextIndex], &can_msg->data[1], currentBytes);
         me->nextIndex += currentBytes;

         if (me->nextIndex == me->current_msg->len) {
            /* whole message has been received -> return to caller */
            me->state = kwptp_state_idle;
            if (NULL != me->current_msg->callback) {
               me->current_msg->callback(me->current_msg->callback_data, me->current_msg);
            }
         } else {
            /* only partial message has been received so far -> wait for more consecutive frames */
            me->state = kwptp_state_expect_response;
            me->timer = me->current_msg->timeout;
         }

         break;
      }
   }

   return RC_SUCCESS;
}

static DWORD WINAPI kwptp_timer_thread(LPVOID par) {
   struct kwptp *me = (struct kwptp*) par;   /* just botch the pointer to the expected object.*/

   me->timer_thread_run = TRUE;

   while (me->timer_thread_run) {
      if (me->timer > 0) {
         me->timer--;
         if (me->timer == 0)                 /* timeout elapsed */
         {
            me->state = kwptp_state_idle;
            me->current_msg->state = kwptp_msg_state_timed_out;
            if (NULL != me->current_msg->callback) {
               me->current_msg->callback(me->current_msg->callback_data, me->current_msg);
            }
         }
      }
      Sleep(10);
   }
   return NO_ERROR;
}

static RC kwptp_create_timer_thread(struct kwptp *me) {
   me->timer_thread_handle = CreateThread(0, 0x1000, kwptp_timer_thread, (LPVOID) me, 0, &me->timer_thread_id);
   return RC_SUCCESS;
}

/***********************************************************************************************************************
 *                             Copyright 2010 Linde Material Handling. All rights reserved.                             *
 ***********************************************************************************************************************/
